﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace WheelOfCivilization
{
    /// <summary>
    /// Camera Control Class
    /// </summary>
    public class Camera
    {
        public enum RotationDirection { AntiClockwise, Clockwise }
        public enum ZoomDirection { Out, In }

        protected float _zoom; // Camera Zoom
        protected float _rotation; // Camera Rotation

        protected Matrix _transform; // Matrix Transform

        protected Vector2 _pos; // Camera Position
        protected Vector2 _defaultPos; // Camera Default Position (For Resetting)

        /// <summary>
        /// <para>Camera creation method</para>
        /// <para>Sets the initial position of the center of the game screen</para>
        /// </summary>
        /// <param name="halfWidth">Half the width of the game screen</param>
        /// <param name="halfHeight">Half the height of the game screen</param>
        public Camera(int halfWidth, int halfHeight)
        {
            _zoom = 1.0f;
            _rotation = 0.0f;
            _defaultPos = new Vector2(halfWidth, halfHeight); //center to map
            _pos = _defaultPos; //for the purposes of resetting the camera
        }

        /// <summary>
        /// Gets or sets the camera zoom by an exact value
        /// </summary>
        public float ZoomLevel
        {
            get { return _zoom; }
            set { _zoom = value; if (_zoom < CameraSettings.MinZoom) _zoom = CameraSettings.MinZoom; if (_zoom > CameraSettings.MaxZoom) _zoom = CameraSettings.MaxZoom; }
            // WARNING: Negative zoom will flip image - DO NOT REMOVE THIS CODE, IT WILL F*** THINGS UP!
        }

        public void Zoom(ZoomDirection z)
        {
            if (z == ZoomDirection.In)
            {
                ZoomLevel += CameraSettings.ZoomAmount;

                if (ZoomLevel > CameraSettings.MaxZoom)
                    ZoomLevel = CameraSettings.MaxZoom;
            }
            else // If Zoom Direction = Out
            {
                ZoomLevel -= CameraSettings.ZoomAmount;

                if (ZoomLevel < CameraSettings.MinZoom)
                    ZoomLevel = CameraSettings.MinZoom;
            }
        }

        /// <summary>
        /// Gets or sets the camera rotation by an exact value
        /// </summary>
        public float Rotation
        {
            get { return _rotation; }
            set { _rotation = value; }
        }

        /// <summary>
        /// Rotates the camera by a single degree in the specified direction
        /// </summary>
        /// <param name="r">The direction in which to rotate</param>
        public void Rotate(RotationDirection r)
        {
            //Each movement checks if the rotation value is great or lower than 360 or 0 to flip it to the other end of the rotational circle
            if (r == RotationDirection.AntiClockwise)
            {
                Rotation -= CameraSettings.RotationAmount;

                if (Rotation < 0.0f)
                    Rotation = CameraSettings.MaxRotation;
            }
            else // If Rotation Direction = Clockwise
            {
                Rotation += CameraSettings.RotationAmount;

                if (Rotation > CameraSettings.MaxRotation)
                    Rotation = 0.0f;
            }
        }

        /// <summary>
        /// Move the camera position
        /// </summary>
        /// <param name="amount">Amount to move the camera by</param>
        public void Move(Vector2 amount)
        {
            _pos += amount;
        }

        /// <summary>
        /// Move the camera position
        /// </summary>
        /// <param name="x">Amount to change the x value by</param>
        /// <param name="y">Amount to change the y value by</param>
        public void Move(int x, int y)
        {
            Vector2 v = new Vector2(x, y);
            Move(v);
        }

        /// <summary>
        /// Gets or sets the camera position
        /// </summary>
        public Vector2 Pos
        {
            get { return _pos; }
            set { _pos = value; }
        }

        /// <summary>
        /// Resets the cameras zoom, rotation and position back to their default values
        /// </summary>
        public void Reset()
        {
            Pos = _defaultPos;
            Rotation = 0.0f;
            ZoomLevel = 1.0f;
        }

        /// <summary>
        /// Gets the camera view (transformation matrix)
        /// </summary>
        /// <param name="graphicsDevice"></param>
        /// <returns>Transformation matrix to draw independant of camera location</returns>
        public Matrix getCameraView(GraphicsDevice graphicsDevice)
        {
            Viewport viewPort = graphicsDevice.Viewport;

            _transform = Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *
                         Matrix.CreateRotationZ(Rotation) *
                         Matrix.CreateScale(new Vector3(ZoomLevel, ZoomLevel, 1)) *
                         Matrix.CreateTranslation(new Vector3(viewPort.Width * 0.5f, viewPort.Height * 0.5f, 0));

            return _transform;
        }
    }

    /// <summary>
    /// Defintions and Methods for Camera Settings
    /// </summary>
    public static class CameraSettings
    {
        private static float _maxZoom = 10.0f; //max zoom amount
        private static float _minZoom = 0.1f; //min zoom amount
        private static float _zoomAmount = 0.1f; //zoom amount (0.1x)

        private static float _rotationAmount = 0.0174532924f; //rotation amount (1 degree)
        private static float _maxRotation = (float)MathHelper.Pi * 2.0f; // float value of 360 degrees

        /// <summary>
        /// Gets the Maximum zoom amount allowed
        /// </summary>
        public static float MaxZoom { get { return _maxZoom; } }
        /// <summary>
        /// Gets the Minimum zoom amount allowed
        /// </summary>
        public static float MinZoom { get { return _minZoom; } }
        /// <summary>
        /// Gets the amount to zoom the camera by
        /// </summary>
        public static float ZoomAmount { get { return _zoomAmount; } }

        /// <summary>
        /// Gets the amount to rotate the camera by
        /// </summary>
        public static float RotationAmount { get { return _rotationAmount; } }
        /// <summary>
        /// Gets the maximum rotation angle allowed
        /// </summary>
        public static float MaxRotation { get { return _maxRotation; } }

        /// <summary>
        /// Defintions and methods for Camera Control Keys
        /// </summary>
        internal class ControlKeys
        {
            private static Keys _zoomInKey = Keys.PageUp;
            private static Keys _zoomOutKey = Keys.PageDown;

            private static Keys _rotateClockwiseKey = Keys.End;
            private static Keys _rotateAntiClockwiseKey = Keys.Delete;

            private static Keys _moveUpKey = Keys.Up;
            private static Keys _moveDownKey = Keys.Down;
            private static Keys _moveLeftKey = Keys.Left;
            private static Keys _moveRightKey = Keys.Right;

            private static Keys _resetCameraKey = Keys.Home;

            /// <summary>
            /// Gets the Zoom In key
            /// </summary>
            public static Keys ZoomIn { get { return _zoomInKey; } }
            /// <summary>
            /// Gets the Zoom Out key
            /// </summary>
            public static Keys ZoomOut { get { return _zoomOutKey; } }

            /// <summary>
            /// Gets the Rotate Clockwise key
            /// </summary>
            public static Keys RotateClockwise { get { return _rotateClockwiseKey; } }
            /// <summary>
            /// Gets the Anti Clockwise key
            /// </summary>
            public static Keys RotateAntiClockwise { get { return _rotateAntiClockwiseKey; } }

            /// <summary>
            /// Gets the Move Up key
            /// </summary>
            public static Keys MoveUp { get { return _moveUpKey; } }
            /// <summary>
            /// Gets the Move Down key
            /// </summary>
            public static Keys MoveDown { get { return _moveDownKey; } }
            /// <summary>
            /// Gets the Move Left key
            /// </summary>
            public static Keys MoveLeft { get { return _moveLeftKey; } }
            /// <summary>
            /// Gets the Move Right key
            /// </summary>
            public static Keys MoveRight { get { return _moveRightKey; } }

            /// <summary>
            /// Gets the Camera Reset key
            /// </summary>
            public static Keys ResetCamera { get { return _resetCameraKey; } }
        }
    }
}